package com.project.thisDatabase.base.db;

import com.project.annotation.data.DbTable;
import com.project.annotation.data.Sort;
import com.project.annotation.data.Symbol;
import com.project.annotation.enums.data.ConditionType;
import com.project.annotation.enums.data.SymbolCode;
import com.project.thisCommon.common.ConvertEntity;
import com.project.thisDatabase.base.model.data.*;
import com.project.thisDatabase.base.model.data.LimitItem;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class MysqlText implements SqlText {

    @Override
    public <U> String Select(Condition<U> item, List pm) throws Exception {
        return Select(new String[] {"*"}, item, pm);
    }

    @Override
    public <U> String Select(String[] cols, Condition<U> item, List pm) throws Exception {
        return Select(cols, item, null, null, pm);
    }

    @Override
    public <U> String Select(String[] cols, Condition<U> item, Integer start, Integer max, List pm) throws Exception {
        StringBuilder sb = new StringBuilder();
        String tableName = item.getBody().getClass().getName();

        if (item.getBody().getClass().isAnnotationPresent(DbTable.class)) {
            DbTable dbTable = item.getBody().getClass().getAnnotation(DbTable.class);
            tableName = dbTable.value();
        }

        StringBuilder where = buildWhere(item, pm);
        StringBuilder order = buildSort(item);

        LimitItem limit = new LimitItem(start, max);

        sb.append("select ");
        sb.append(String.join(",", cols));
        sb.append(" from ");
        sb.append(tableName);

        if (StringUtils.isNotBlank(where)) {
            sb.append(" where ");
            sb.append(where);
        }

        if (StringUtils.isNotBlank(order)) {
            sb.append(" order by ");
            sb.append(order);
        }

        if (limit.isLimit()) {
            sb.append(" limit ");
            sb.append(limit);
        }

        return sb.toString();
    }

    public <T, U> String Update(T change, Condition<U> item, List pm) throws Exception {
        String tableName = item.getBody().getClass().getName();
        if (item.getBody().getClass().isAnnotationPresent(DbTable.class)) {
            DbTable dbTable = item.getBody().getClass().getAnnotation(DbTable.class);
            tableName = dbTable.value();
        }

        StringBuilder field = buildUpdate(change, pm);
        StringBuilder where = buildWhere(item, pm);

        StringBuilder sb = new StringBuilder();
        sb.append("update ");
        sb.append(tableName);
        sb.append(" set ");
        sb.append(field);
        if (StringUtils.isNotBlank(where)) {
            sb.append(" where ");
            sb.append(where);
        }

        return sb.toString();
    }

    public <T> String Insert(T change, List pm) throws Exception {
        StringBuilder sb = new StringBuilder();

        String tableName = change.getClass().getName();
        if (change.getClass().isAnnotationPresent(DbTable.class)) {
            DbTable dbTable = change.getClass().getAnnotation(DbTable.class);
            tableName = dbTable.value();
        }

        sb.append("insert into ");
        sb.append(tableName);
        sb.append(buildInsert(change, pm));

        return sb.toString();
    }

    public <U> String Delete(Condition<U> item, List pm) throws Exception {
        StringBuilder sb = new StringBuilder();
        String tableName = item.getBody().getClass().getName();
        if (item.getBody().getClass().isAnnotationPresent(DbTable.class)) {
            DbTable dbTable = item.getBody().getClass().getAnnotation(DbTable.class);
            tableName = dbTable.value();
        }

        StringBuilder where = buildWhere(item, pm);

        sb.append("delete from ");
        sb.append(tableName);
        if (StringUtils.isNotBlank(where)) {
            sb.append(" where ");
            sb.append(where);
        }

        return sb.toString();
    }

    protected <T> StringBuilder buildInsert(T item, List params) throws Exception {
        StringBuilder sb = new StringBuilder();
        List<String> cols = new ArrayList<>();
        List<String> values = new ArrayList<>();
        Map cond = ConvertEntity.CovertToMap(item);
        cond.forEach((k, v)->{
            if (null != v) {
                cols.add(k.toString());
                values.add("?");
                params.add(v);
            }
        });
        sb.append("(");
        sb.append(StringUtils.join(cols, ","));
        sb.append(")");
        sb.append(" values ");
        sb.append("(");
        sb.append(StringUtils.join(values, ","));
        sb.append(")");

        return sb;
    }

    protected <T> StringBuilder buildUpdate(T item, List params) throws Exception {
        if (null == item) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        Map cond = ConvertEntity.CovertToMap(item);
        List<UpdateItem> update = new ArrayList<>();
        cond.forEach((k, v)->{
            params.add(v);
            update.add(new UpdateItem(k.toString(), new Object[] {v}));
        });

        sb.append(StringUtils.join(update, ","));
        return sb;
    }

    protected StringBuilder buildSort(Condition item) throws Exception {
        if (null == item) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        List<SortItem> sorts = new ArrayList<>();
        ConvertEntity.CovertToMap(item.getBody(), true, c->{
            if (c.getEntityCollect().getField().isAnnotationPresent(Sort.class)) {
                Sort sort = c.getEntityCollect().getField().getAnnotation(Sort.class);
                String name = StringUtils.isNotBlank(sort.value()) ? sort.value() : c.getEntityCollect().getName();
                sorts.add(new SortItem(name, sort.type(), sort.index()));
            }

            return c.getValue();
        });

        sorts.sort((a, b)->a.getIndex() - b.getIndex());

        sb.append(StringUtils.join(sorts, ","));
        return sb;
    }

    protected StringBuilder buildWhere(Condition item, List params) throws Exception {
        if (null == item) {
            return null;
        }

        StringBuilder sb = new StringBuilder();

        Map cond = ConvertEntity.CovertToMap(item.getBody(), false, c->{
            if (null != c.getValue()) {
                List p = ConvertEntity.ObjectToList(c.getValue());
                params.addAll(p);
                if (c.getEntityCollect().getField().isAnnotationPresent(Symbol.class)) {
                    Symbol symbol = c.getEntityCollect().getField().getAnnotation(Symbol.class);
                    String name = StringUtils.isNotBlank(symbol.value()) ? symbol.value() : c.getEntityCollect().getName();
                    return new FieldItem(name, symbol.type(), p.toArray());
                }

                return new FieldItem(c.getEntityCollect().getName(), SymbolCode.EQUAL, p.toArray());
            }

            return c.getValue();
        });

        sb.append("(");
        sb.append("(");
        StringBuilder its = new StringBuilder();
        cond.values().forEach(f->{
            if (null != f) {
                if (its.length() > 0) {
                    its.append(" and ");
                }
                its.append(f);
            }
        });
        sb.append(its);
        sb.append(")");
        StringBuilder child = buildWhere(item.getChild(), params);
        if (StringUtils.isNotBlank(child)) {
            sb.append(mark(item.getType()));
            sb.append(child);
        }
        sb.append(")");

        return sb;
    }

    protected String mark(Integer type) {
        if (ConditionType.and == type) {
            return " and ";
        } else if (ConditionType.or == type) {
            return " or ";
        } else if (ConditionType.not == type) {
            return " not ";
        }

        return " and ";
    }
}
